/*
 * Copyright (C) 2010 mAPPn.Inc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.mol.market.common.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;

import android.content.Context;
import android.graphics.Bitmap;
import android.text.TextUtils;

/**
 * Image Cache Manager
 * 
 * For caching something like drawables, objects...
 * 
 * @author andrew.wang
 * 
 */
public class CacheManager {

    private static CacheManager mInstance;

    // 首页轮播图缓存时间 ：3天
    private static final int IMAGE_TIMEOUT = 259200000;
    
    private static final String ICON_CACHE_NAME = "image.info";
    
    // the max cached icon in the memory
    private static int MAX_IMAGE_IN_L1_MEMORY = 50;
    
    // 线程锁
    private final Object mMutex = new Object();

    // cache in the memory
    private final LinkedHashMap<Integer, Bitmap> mDrawableMap = new LinkedHashMap<Integer, Bitmap>();

    private SoftReference<LinkedHashMap<Integer, Bitmap>> mL1Cache =
        new SoftReference<LinkedHashMap<Integer, Bitmap>>(mDrawableMap);
    

    private HashMap<Integer, String> mL2CacheMap = new HashMap<Integer, String>();

    @SuppressWarnings("unchecked")
    private CacheManager(Context context) {

        final File cacheDir = context.getCacheDir();
        final File target = new File(cacheDir, ICON_CACHE_NAME);
        if (target.exists()) {
            try {
                ObjectInputStream ois = new ObjectInputStream(new FileInputStream(target));
                Object result = ois.readObject();
                ois.close();

                HashMap<Integer, String> obj = (HashMap<Integer, String>) result;
                if (obj != null) {
                    mL2CacheMap = obj;
                }
                
            } catch (IOException e) {
                Utils.E("Error when get icon cache", e);
            } catch (ClassNotFoundException e) {
                Utils.E("Error when get icon cache", e);
            }
        }
        // 可用的总内存大小
        long maxMemory = Runtime.getRuntime().maxMemory();
        if (maxMemory <= 20000000) {
            MAX_IMAGE_IN_L1_MEMORY = 20;
        } else if(maxMemory <= 24000000) {
            MAX_IMAGE_IN_L1_MEMORY = 30;
        }
        Utils.D("cache size >>>>>>>>>>>>>" + MAX_IMAGE_IN_L1_MEMORY);
    }

    public synchronized static CacheManager getInstance(Context context) {
        if (mInstance == null) {
            mInstance = new CacheManager(context);
        }
        return mInstance;
    }

    /**
     * Check whether the drawable had been cached
     * 
     * @param key
     *            drawable uri
     * @return ture : exist false : not exist
     */
    public boolean existsDrawable(String url) {

        int key = url.hashCode();
        
        if (mL2CacheMap.containsKey(key)) {
            return true;
        }
        
        return false;
    }

    /**
     * 开始缓存图片
     */
    public synchronized void cacheDrawable(Context context, String url, Bitmap bmp) {

        if (bmp == null) {
            return;
        }

        final int key = url.hashCode();

        LinkedHashMap<Integer, Bitmap> l1 = mL1Cache.get();

        if (l1 == null) {
            mL1Cache = new SoftReference<LinkedHashMap<Integer, Bitmap>>(mDrawableMap);
        }

        if (l1.size() < MAX_IMAGE_IN_L1_MEMORY) {
            l1.put(key, bmp);
        } else {
            // reach here means L1 is full, reorder current stack
            Iterator<Integer> iterator = l1.keySet().iterator();
            if (iterator.hasNext()) {
                // recycle the bitmap
                Integer oldKey = (Integer) iterator.next();
                l1.remove(oldKey);

                // reorder the new image into L1 cache stack
                l1.put(key, bmp);
            }
        }
        
        // 如果Key不存在于2级缓存，则避免重复往SD卡写数据
        if (!mL2CacheMap.containsKey(key)) {
            final long expire = System.currentTimeMillis() + IMAGE_TIMEOUT;
            final String fileName = key + "." + expire;
            synchronized (mMutex) {
                mL2CacheMap.put(key, fileName);
            }
            ImageUtils.saveBitmapToSdcard(context, fileName, bmp);
        }
    }
    
    /**
     * 直接缓存到SD卡上
     */
    public synchronized void cacheDrawableToL2(Context context, String url, Bitmap bmp) {
        
        if (bmp == null) {
            return;
        }
        final int key = url.hashCode();
        
        if (!mL2CacheMap.containsKey(key)) {
            
            final long expire = System.currentTimeMillis() + IMAGE_TIMEOUT;
            final String fileName  = key + "." + expire;
            synchronized (mMutex) {
                mL2CacheMap.put(key, fileName);
            }
            ImageUtils.saveBitmapToSdcard(context, fileName, bmp);
        }
    }
    
    /**
     * 获取文件映射名
     */

    public String getFileName(String url) {

        if (TextUtils.isEmpty(url) || mL2CacheMap == null) {
            return null;
        }

        return mL2CacheMap.get(url.hashCode());
    }
    
    /**
     * 直接缓存到file下
     */
    public synchronized void cacheDrawableToFile(Context context, String url, Bitmap bmp) {

        if (bmp == null) {
            return;
        }
        final int key = url.hashCode();
        final long expire = System.currentTimeMillis() + IMAGE_TIMEOUT;
        ImageUtils.saveBitmapToFile(context, (key + "." + expire), bmp);
    }
    
    /**
     * 从缓存中获取图片
     * 
     * @param url
     *            图片的HTTP资源的地址
     */

    public Bitmap getDrawableFromCache(Context context, String url) {

        final int key = url.hashCode();

        // retrieve bitmap from L1 cache
        LinkedHashMap<Integer, Bitmap> l1 = mL1Cache.get();
        if (l1 != null) {
            Bitmap bmp = l1.get(key);
            if (bmp != null && !bmp.isRecycled()) {
                return bmp;
            }
        }
        
        return null;
    }

    /**
     * 当内存不足时清除所有缓存包括应用退出时
     */
    public void clearFromMemory(Context context) {

        LinkedHashMap<Integer, Bitmap> cache = mL1Cache.get();
        if (cache != null) {
            try {
                cache.clear();
            } catch (Exception e) {
                Utils.E("clear cache exception", e);
            }
        }
        
        if (mL2CacheMap != null) {
            synchronized (mMutex) {
                final File cacheDir = context.getCacheDir();
                try {
                    FileOutputStream fos = new FileOutputStream(new File(cacheDir, ICON_CACHE_NAME));
                    ObjectOutputStream oos = new ObjectOutputStream(fos);
                    oos.writeObject(mL2CacheMap);
                    oos.close();
                } catch (IOException e) {
                    Utils.E("Error when save icon cache", e);
                }
            }
        }
    }
    
    /**
     * 清除过期的缓存文件
     */
    public void clearExpiredFile(final Context context) {
        Thread clearTask = new Thread() {
            @Override
            public void run() {
                File cacheDirectory = context.getCacheDir();
                if (cacheDirectory.exists()) {
                    String[] files = cacheDirectory.list();
                    
                    if (files == null || files.length == 0) {
                        return;
                    }
                    
                    long currentTime = System.currentTimeMillis();
                    for (String file : files) {
                        int splitterPos = file.lastIndexOf(".");
                        if (splitterPos <= 0) {
                            continue;
                        }
                        String name = file.substring(0, splitterPos);
                        String extension = file.substring(splitterPos + 1);
                        long expire = Utils.getLong(extension);
                        if (expire > 0 && expire < currentTime) {
                            new File(cacheDirectory, file).delete();
                            mL2CacheMap.remove(name);
                        }
                    }
                }
            }
        };
        clearTask.setPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
        clearTask.start();
    }
}
